package team.archai.nexus.boot.web.shiro.realm;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import team.archai.nexus.boot.auth.entity.JwtUser;
import team.archai.nexus.boot.auth.model.AuthenticatedInfo;
import team.archai.nexus.boot.auth.token.IAuth;
import team.archai.nexus.boot.auth.util.AuthUtil;
import team.archai.nexus.boot.tool.Tool;
import team.archai.nexus.boot.web.service.AuthService;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Set;

/**
 * @program: tiny-dust-boot-build
 * @description: realm设置
 * @author: lishibin
 * @create: 2020-06-26 22:59
 **/
@Component
@ConditionalOnProperty(name = "archai-nexus.auth.type", havingValue = "shiro")
public class ArchaiNexusRealm extends AuthorizingRealm {

    private final Log log = LogFactory.getLog(ArchaiNexusRealm.class);

    @Resource
    private AuthService<?> authService;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof IAuth;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        log.info("===============Shiro validate ============");
        if (principals != null) {
            AuthenticatedInfo authenticatedInfo = (AuthenticatedInfo) principals.getPrimaryPrincipal();
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 设置用户拥有的角色集合，比如“admin,test”
        Set<String> roleSet = new HashSet<>();
        info.setRoles(roleSet);
        // 设置用户拥有的权限集合，比如“sys:role:add,sys:user:add”
        Set<String> permissionSet = new HashSet<>();
        info.addStringPermissions(permissionSet);
        log.info("===============Shiro validated==============");
        return info;
    }

    /**
     * 用户信息认证是在用户进行登录的时候进行验证(不存redis)
     * 也就是说验证用户输入的账号和密码是否正确，错误抛出异常
     *
     * @param auth 用户登录的账号密码信息
     * @return 返回封装了用户信息的 AuthenticationInfo 实例
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        Object token = auth.getCredentials();
        IAuth response = AuthUtil.parse(Tool.toStr(token));
        AuthenticatedInfo authenticatedInfo = response.getPrincipal();
        JwtUser jwtUser = authService.loadUserByNameAndPassword(authenticatedInfo.getSubject(), String.valueOf(authenticatedInfo.getCredential()));
        return new SimpleAuthenticationInfo(response, token, jwtUser.getUserName());
    }


    /**
     * JWTToken刷新生命周期 （实现： 用户在线操作不掉线功能）
     * 1、登录成功后将用户的JWT生成的Token作为k、v存储到cache缓存里面(这时候k、v值一样)，缓存有效期设置为Jwt有效时间的2倍
     * 2、当该用户再次请求时，通过JWTFilter层层校验之后会进入到doGetAuthenticationInfo进行身份验证
     * 3、当该用户这次请求jwt生成的token值已经超时，但该token对应cache中的k还是存在，则表示该用户一直在操作只是JWT的token失效了，程序会给token对应的k映射的v值重新生成JWTToken并覆盖v值，该缓存生命周期重新计算
     * 4、当该用户这次请求jwt在生成的token值已经超时，并在cache中不存在对应的k，则表示该用户账户空闲超时，返回用户信息已失效，请重新登录。
     * 注意： 前端请求Header中设置Authorization保持不变，校验有效性以缓存中的token为准。
     * 用户过期时间 = Jwt有效时间 * 2。
     *
     * @param userName
     * @param passWord
     * @return
     */
    public boolean jwtTokenRefresh(String token, String userName, String passWord) {
        String cacheToken = "";
        //String.valueOf(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token));
        //获取缓存token
        if (Tool.isNotEmpty(cacheToken)) {
            // 校验token有效性
//            if (!JwtUtil.verify(cacheToken, userName, passWord)) {
//                String newAuthorization = JwtUtil.sign(userName, passWord);
//                // 设置超时时间
//                redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, newAuthorization);
//                redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
//                log.info("——————————用户在线操作，更新token保证不掉线—————————jwtTokenRefresh——————— " + token);
//            }
            //update-begin--Author:scott  Date:20191005  for：解决每次请求，都重写redis中 token缓存问题
//			else {
//				// 设置超时时间
//				redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, cacheToken);
//				redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);
//			}
            //update-end--Author:scott  Date:20191005   for：解决每次请求，都重写redis中 token缓存问题
            return true;
        }
        return false;
    }

    /**
     * 清除当前用户的权限认证缓存
     *
     * @param principals 权限信息
     */
    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }
}
